<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Qwen 聊天助手</title>
  
  <!-- 使用CDN导入Element Plus -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/element-plus/dist/index.css" />
  
  <!-- 导入自定义CSS -->
  <link rel="stylesheet" href="/static/css/chat.css" />
  
  <script src="https://cdn.jsdelivr.net/npm/vue@3.2.47/dist/vue.global.prod.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/element-plus/dist/index.full.min.js"></script>
  
  <!-- 导入markdown-it - 功能强大的Markdown解析器 -->
  <script src="https://cdn.jsdelivr.net/npm/markdown-it@13.0.2/dist/markdown-it.min.js"></script>
  <!-- 导入DOMPurify用于安全的HTML渲染 -->
  <script src="https://cdn.jsdelivr.net/npm/dompurify@3.0.5/dist/purify.min.js"></script>
  
  <style>
    [v-cloak] {
      display: none;
    }
    
    /* 用户信息区域样式 */
    .user-info {
      position: absolute;
      bottom: 0;
      left: 0;
      width: 100%;
      padding: 10px;
      display: flex;
      align-items: center;
      background: linear-gradient(180deg, #2c3e50 0%, #34495e 100%);
      border-top: 1px solid rgba(255, 255, 255, 0.1);
    }
    
    .dark-mode .user-info {
      background: linear-gradient(180deg, #1a1a1a 0%, #2c2c2c 100%);
      border-top: 1px solid #444;
    }
    
    .user-avatar {
      width: 28px;
      height: 28px;
      border-radius: 50%;
      background-color: #e0e0e0;
      display: flex;
      align-items: center;
      justify-content: center;
      margin-right: 8px;
      font-size: 16px;
      flex-shrink: 0;
    }
    
    .dark-mode .user-avatar {
      background-color: #444;
    }
    
    /* 用户详情容器 */
    .user-details {
      flex: 1;
      display: flex;
      flex-direction: column;
      gap: 2px;
      min-width: 0;
      overflow: hidden;
    }
    
    .user-name {
      font-size: 14px;
      font-weight: 500;
      color: #e0e0e0;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    
    .user-token {
      font-size: 11px;
      font-weight: 400;
      color: #b0b0b0;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      font-family: 'Courier New', monospace;
    }
    
    .dark-mode .user-name {
      color: #e0e0e0;
    }
    
    .dark-mode .user-token {
      color: #a0a0a0;
    }
    
    /* 模型选择器样式 */
    .model-selector {
      flex-shrink: 0;
    }
    
    .model-select {
      background: rgba(255, 255, 255, 0.1);
      border: 1px solid rgba(255, 255, 255, 0.2);
      color: #e0e0e0;
      padding: 4px 8px;
      border-radius: 8px;
      font-size: 12px;
      cursor: pointer;
      outline: none;
      transition: all 0.2s ease;
      min-width: 100px;
    }
    
    .model-select:hover {
      background: rgba(255, 255, 255, 0.15);
      border-color: rgba(255, 255, 255, 0.3);
    }
    
    .model-select:focus {
      background: rgba(255, 255, 255, 0.2);
      border-color: #667eea;
      box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2);
    }
    
    .model-select option {
      background: #2c3e50;
      color: #e0e0e0;
      padding: 4px 8px;
    }
    
    .dark-mode .model-select {
      background: rgba(255, 255, 255, 0.05);
      border-color: #444;
    }
    
    .dark-mode .model-select:hover {
      background: rgba(255, 255, 255, 0.1);
      border-color: #555;
    }
    
    .dark-mode .model-select option {
      background: #1a1a1a;
      color: #e0e0e0;
    }
    
    /* 调整侧边栏布局，为用户信息区域留出空间 */
    .sidebar {
      position: relative;
      padding-bottom: 50px; /* 为用户信息区域留出空间 */
    }
    
    .chat-history {
      overflow-y: auto;
      max-height: calc(100% - 50px);
    }
    
    /* 推理过程样式 */
    .reasoning-content {
      background: rgba(128, 128, 128, 0.1);
      border: 1px solid rgba(128, 128, 128, 0.3);
      border-radius: 8px;
      padding: 12px;
      margin-bottom: 12px;
      font-size: 12px; /* 比正式内容小两个字号 */
      line-height: 1.5;
      animation: fadeIn 0.3s ease-in;
    }
    
    .dark-mode .reasoning-content {
      background: rgba(128, 128, 128, 0.05);
      border-color: rgba(128, 128, 128, 0.2);
    }
    
    .reasoning-header {
      display: flex;
      align-items: center;
      margin-bottom: 8px;
      font-weight: 500;
      color: #666666;
    }
    
    .dark-mode .reasoning-header {
      color: #cccccc;
    }
    
    .reasoning-icon {
      margin-right: 6px;
      font-size: 16px;
      animation: pulse 1.5s infinite;
    }
    
    .reasoning-title {
      font-size: 11px; /* 比正式内容小两个字号 */
    }
    
    .reasoning-text {
      color: #555555;
      white-space: pre-wrap;
      word-wrap: break-word;
      max-height: 200px;
      overflow-y: auto;
      scroll-behavior: smooth;
      scrollbar-width: thin;
      scrollbar-color: rgba(128, 128, 128, 0.3) transparent;
      font-size: 12px; /* 比正式内容小两个字号 */
    }
    
    .reasoning-text::-webkit-scrollbar {
      width: 6px;
    }
    
    .reasoning-text::-webkit-scrollbar-track {
      background: transparent;
    }
    
    .reasoning-text::-webkit-scrollbar-thumb {
      background-color: rgba(128, 128, 128, 0.3);
      border-radius: 3px;
    }
    
    .reasoning-text::-webkit-scrollbar-thumb:hover {
      background-color: rgba(128, 128, 128, 0.5);
    }
    
    .dark-mode .reasoning-text {
      color: #bbbbbb;
    }
    
    /* Markdown 内容样式 */
    .markdown-content {
      line-height: 1.6;
      color: #333;
      word-wrap: break-word;
    }
    
    .dark-mode .markdown-content {
      color: #e0e0e0;
    }
    
    /* 表格样式 */
    .markdown-content table {
      border-collapse: collapse;
      width: 100%;
      margin: 12px 0;
      font-size: 14px;
      background-color: #fff;
      border-radius: 6px;
      overflow: hidden;
      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    }
    
    .dark-mode .markdown-content table {
      background-color: #2a2a2a;
      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
    }
    
    .markdown-content th,
    .markdown-content td {
      border: 1px solid #ddd;
      padding: 8px 12px;
      text-align: left;
      vertical-align: top;
    }
    
    .dark-mode .markdown-content th,
    .dark-mode .markdown-content td {
      border-color: #444;
    }
    
    .markdown-content th {
      background-color: #f8f9fa;
      font-weight: 600;
      color: #495057;
    }
    
    .dark-mode .markdown-content th {
      background-color: #3a3a3a;
      color: #e0e0e0;
    }
    
    .markdown-content tr:nth-child(even) {
      background-color: #f8f9fa;
    }
    
    .dark-mode .markdown-content tr:nth-child(even) {
      background-color: #333;
    }
    
    .markdown-content tr:hover {
      background-color: #e9ecef;
    }
    
    .dark-mode .markdown-content tr:hover {
      background-color: #404040;
    }
    
    /* 标题样式 */
    .markdown-content h1,
    .markdown-content h2,
    .markdown-content h3,
    .markdown-content h4,
    .markdown-content h5,
    .markdown-content h6 {
      margin: 16px 0 8px 0;
      font-weight: 600;
      line-height: 1.25;
    }
    
    .markdown-content h1 { font-size: 1.5em; border-bottom: 1px solid #eee; padding-bottom: 8px; }
    .markdown-content h2 { font-size: 1.3em; }
    .markdown-content h3 { font-size: 1.2em; }
    .markdown-content h4 { font-size: 1.1em; }
    .markdown-content h5 { font-size: 1em; }
    .markdown-content h6 { font-size: 0.9em; }
    
    .dark-mode .markdown-content h1 {
      border-bottom-color: #444;
    }
    
    /* 列表样式 */
    .markdown-content ul,
    .markdown-content ol {
      margin: 8px 0;
      padding-left: 20px;
    }
    
    .markdown-content li {
      margin: 4px 0;
    }
    
    /* 代码块样式 */
    .markdown-content pre {
      background-color: #f8f9fa;
      border: 1px solid #e9ecef;
      border-radius: 6px;
      padding: 12px;
      overflow-x: auto;
      margin: 12px 0;
      font-family: 'Courier New', Courier, monospace;
      font-size: 0.9em;
      line-height: 1.5;
      white-space: pre-wrap;
      word-break: break-all;
    }
    
    .dark-mode .markdown-content pre {
      background-color: #2a2a2a;
      border-color: #444;
      color: #e0e0e0;
    }
    
    .markdown-content code {
      background-color: #f1f3f4;
      padding: 2px 4px;
      border-radius: 3px;
      font-family: 'Courier New', Courier, monospace;
      font-size: 0.9em;
    }
    
    .dark-mode .markdown-content code {
      background-color: #3a3a3a;
      color: #e0e0e0;
    }
    
    .markdown-content pre code {
      background-color: transparent;
      padding: 0;
      border-radius: 0;
      display: block;
      width: 100%;
    }
    
    /* 引用样式 */
    .markdown-content blockquote {
      border-left: 4px solid #ddd;
      margin: 12px 0;
      padding: 8px 16px;
      background-color: #f8f9fa;
      color: #666;
    }
    
    .dark-mode .markdown-content blockquote {
      border-left-color: #555;
      background-color: #2a2a2a;
      color: #ccc;
    }
    
    /* 链接样式 */
    .markdown-content a {
      color: #007bff;
      text-decoration: none;
    }
    
    .markdown-content a:hover {
      text-decoration: underline;
    }
    
    .dark-mode .markdown-content a {
      color: #66b3ff;
    }
    
    /* 分隔线样式 */
    .markdown-content hr {
      border: none;
      border-top: 1px solid #eee;
      margin: 16px 0;
      height: 1px;
      display: block;
      width: 100%;
      background-color: #eee;
    }
    
    .dark-mode .markdown-content hr {
      border-top-color: #444;
      background-color: #444;
    }
    
    /* 任务列表样式 */
    .markdown-content input[type="checkbox"] {
      margin-right: 8px;
    }
    
    /* 输入区域功能按钮样式 */
    .input-wrapper {
      position: relative;
    }
    
    .message-input {
      min-height: 80px !important; /* 增加输入框高度 */
      padding-bottom: 40px !important; /* 为按钮留出空间 */
      resize: vertical;
    }
    
    .input-buttons {
      position: absolute;
      bottom: 8px;
      left: 12px;
      display: flex;
      gap: 8px;
      z-index: 10;
    }
    
    .feature-btn {
      background: rgba(255, 255, 255, 0.9);
      border: 1px solid rgba(0, 0, 0, 0.1);
      border-radius: 16px;
      padding: 6px 12px;
      font-size: 12px;
      color: #666;
      cursor: pointer;
      transition: all 0.2s ease;
      display: flex;
      align-items: center;
      gap: 4px;
      white-space: nowrap;
      backdrop-filter: blur(10px);
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    }
    
    .feature-btn:hover {
      background: rgba(255, 255, 255, 1);
      border-color: rgba(0, 0, 0, 0.2);
      transform: translateY(-1px);
      box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
    }
    
    .feature-btn:active {
      transform: translateY(0);
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    }
    
    .feature-btn.active {
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
      border-color: transparent;
      box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
    }
    
    .feature-btn:disabled {
      opacity: 0.5;
      cursor: not-allowed;
      transform: none;
    }
    
    .deep-thinking-btn.active {
      background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
      box-shadow: 0 4px 12px rgba(240, 147, 251, 0.3);
    }
    
    .online-search-btn.active {
      background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
      box-shadow: 0 4px 12px rgba(79, 172, 254, 0.3);
    }
    
    /* 模型选择器按钮样式 */
    .model-selector-btn {
      position: relative;
      padding: 6px 8px 6px 12px !important;
      min-width: 120px;
    }
    
    .model-selector-btn span {
      margin-right: 4px;
    }
    
    .model-select-input {
      background: transparent;
      border: none;
      color: inherit;
      font-size: 12px;
      cursor: pointer;
      outline: none;
      appearance: none;
      -webkit-appearance: none;
      -moz-appearance: none;
      padding: 2px 4px;
      margin: 0;
      width: 85px;
      border-radius: 8px;
    }
    
    .model-select-input:focus {
      background: rgba(255, 255, 255, 0.1);
      outline: none;
    }
    
    .model-select-input:disabled {
      cursor: not-allowed;
      opacity: 0.7;
    }
    
    /* 优化下拉选项样式 - 圆角矩形设计 */
    .model-select-input option {
      background: #ffffff;
      color: #333333;
      padding: 8px 12px;
      border-radius: 8px;
      margin: 2px 0;
      font-size: 12px;
      font-weight: 500;
      border: 1px solid rgba(0, 0, 0, 0.1);
      transition: all 0.2s ease;
    }
    
    .model-select-input option:hover {
      background: #f8f9fa;
      color: #495057;
      border-color: rgba(0, 0, 0, 0.2);
    }
    
    .model-select-input option:selected {
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
      font-weight: 600;
      border-color: transparent;
    }
    
    /* 深色模式下的选项样式 */
    .dark-mode .model-select-input {
      color: #e0e0e0;
    }
    
    .dark-mode .model-select-input:focus {
      background: rgba(255, 255, 255, 0.05);
    }
    
    .dark-mode .model-select-input option {
      background: #2a2a2a;
      color: #e0e0e0;
      border-color: rgba(255, 255, 255, 0.1);
    }
    
    .dark-mode .model-select-input option:hover {
      background: #3a3a3a;
      color: #f0f0f0;
      border-color: rgba(255, 255, 255, 0.2);
    }
    
    .dark-mode .model-select-input option:selected {
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
      border-color: transparent;
    }
    
    /* 深色模式下的按钮样式 */
    .dark-mode .feature-btn {
      background: rgba(0, 0, 0, 0.7);
      border-color: rgba(255, 255, 255, 0.2);
      color: #e0e0e0;
    }
    
    .dark-mode .feature-btn:hover {
      background: rgba(0, 0, 0, 0.8);
      border-color: rgba(255, 255, 255, 0.3);
    }
    
    /* 动画效果 */
    @keyframes fadeIn {
      from { opacity: 0; transform: translateY(-10px); }
      to { opacity: 1; transform: translateY(0); }
    }
    
    @keyframes pulse {
      0%, 100% { transform: scale(1); }
      50% { transform: scale(1.1); }
    }
  </style>
</head>
<body>
  <div id="app">
    <div class="app-container">
      <!-- 左侧聊天历史 -->
      <div class="sidebar" v-bind:class="{ 'show': showSidebar }">
        <div class="sidebar-header">
          <button class="new-chat-btn" v-on:click="createNewChat">
            <span>💬</span>
            新建对话
          </button>
        </div>
        
        <div class="chat-history">
          <div
            v-for="chat in chatSessions"
            v-bind:key="chat.id"
            class="chat-item"
            v-bind:class="{ 'active': chat.id === currentChatId }"
            v-on:click="switchToChat(chat.id)"
          >
            <div class="chat-title">[[ chat.title ]]</div>
            <div class="chat-time">[[ formatTime(chat.updatedAt) ]]</div>
          </div>
        </div>
        
        <!-- 用户信息区域 -->
        <div class="user-info">
          <div class="user-avatar">👤</div>
          <div class="user-details">
            <div class="user-name">[[ username ]]</div>
            <div class="user-token">[[ token ]]</div>
          </div>
        </div>
      </div>

      <!-- 右侧主聊天区域 -->
      <div class="main-content">
        <div class="main-header">
          <h1 class="main-title">HEC 聊天助手</h1>
          <div class="header-buttons">
            <button 
              class="clear-btn"
              v-bind:disabled="!currentChatId" 
              v-on:click="clearCurrentChat"
            >
              🗑️ 清除对话
            </button>
            <button 
              class="theme-btn"
              v-on:click="toggleDarkMode"
            >
              <span v-if="isDarkMode">☀️ 日间模式</span>
              <span v-else>🌙 夜间模式</span>
            </button>
          </div>
        </div>

        <div class="chat-messages" ref="chatMessages">
          <div v-if="!currentChatId || getCurrentChat()?.messages.length === 0" class="empty-state">
            <div class="empty-icon">💭</div>
            <div class="empty-title">开始新的对话</div>
            <div class="empty-description">在下方输入框中输入您的问题，开始与 AI 助手对话</div>
          </div>
          
          <template v-else>
            <div 
              v-for="message in getCurrentChat().messages" 
              v-bind:key="message.id" 
              class="message-container"
              v-bind:ref="message.role === 'assistant' && message.id === getLastMessageId() ? 'lastAssistantMessage' : null"
            >
              <div 
                class="message" 
                v-bind:class="message.role === 'user' ? 'user-message' : 'assistant-message'"
              >
                <!-- 用户消息靠右显示 -->
                <template v-if="message.role === 'user'">
                  <div class="message-spacer"></div>
                  <div class="message-content">
                    <div class="message-header">
                      <span class="message-author">你</span>
                      <span class="message-time">[[ formatTime(message.timestamp) ]]</span>
                    </div>
                    
                    <div class="message-bubble">
                      [[ message.content ]]
                    </div>
                  </div>
                  
                  <div class="message-avatar user-avatar">
                    👤
                  </div>
                </template>
                
                <!-- 助手消息靠左显示 -->
                <template v-else>
                  <div class="message-avatar assistant-avatar">
                    <img src="/static/images/reboot.png" alt="AI助手" class="avatar-img">
                  </div>
                  
                  <div class="message-content">
                    <div class="message-header">
                      <span class="message-author">AI助手</span>
                      <span class="message-time">[[ formatTime(message.timestamp) ]]</span>
                    </div>
                    
                    <div class="message-bubble">
                      <!-- 推理过程显示区域 -->
                      <div 
                        v-if="message.reasoning_content" 
                        class="reasoning-content"
                      >
                        <div class="reasoning-header">
                          <span class="reasoning-icon">✨</span>
                          <span class="reasoning-title">
                            <span v-if="isLoading && message.id === getLastMessageId()">AI 正在思考...</span>
                            <span v-else>AI 思考过程</span>
                          </span>
                        </div>
                        <div class="reasoning-text">
                          [[ message.reasoning_content ]]
                        </div>
                      </div>
                      
                      <!-- 正式回答内容 -->
                      <div 
                        class="markdown-content" 
                        v-html="renderMarkdown(message.content)"
                        v-show="message.content && message.content.trim()"
                      ></div>
                      
                      <span v-if="isLoading && message.id === getLastMessageId()" class="typing-effect"></span>
                    </div>
                  </div>
                  
                  <div class="message-spacer"></div>
                </template>
              </div>
            </div>
          </template>
        </div>

        <div class="input-area">
          <div class="input-container">
            <div class="input-wrapper">
              <textarea
                v-model="userInput"
                class="message-input"
                placeholder="请输入您的问题..."
                v-on:keydown.enter.prevent="handleEnterKey"
                v-bind:disabled="isLoading"
              ></textarea>
              
              <!-- 功能按钮区域 -->
              <!-- 功能按钮区域 -->
              <div class="input-buttons">
                <!-- 模型选择器 -->
                <div class="feature-btn model-selector-btn" title="选择AI模型">
                  <span>✨</span>
                  <select v-model="selectedModel" class="model-select-input" @change="onModelChange" v-bind:disabled="isLoading">
                    <option value="qwen32b">Qwen3-32B</option>
                    <option value="qwen235b">Qwen3-235B</option>
                    <option value="deepseek-r1">DeepSeek-R1</option>
                    <option value="gpt-4">GPT-4</option>
                  </select>
                </div>
                
                <button 
                  class="feature-btn deep-thinking-btn"
                  v-bind:disabled="isLoading"
                  v-on:click="toggleDeepThinking"
                  v-bind:class="{ 'active': isDeepThinking }"
                  title="深度思考模式"
                >
                  ⚡️ 深度思考
                </button>
                <button 
                  class="feature-btn online-search-btn"
                  v-bind:disabled="isLoading"
                  v-on:click="toggleOnlineSearch"
                  v-bind:class="{ 'active': isOnlineSearch }"
                  title="联网搜索模式"
                >
                  🌐 联网搜索
                </button>
              </div>
            </div>
            <button 
              class="send-btn"
              v-bind:class="{ 'loading': isLoading }"
              v-bind:disabled="!isLoading && (!userInput || !userInput.trim())" 
              v-on:click="sendMessage"
            >
              <span v-if="isLoading" class="loading-square"></span>
              <span v-else>发送</span>
            </button>
          </div>
        </div>
      </div>
    </div>
  </div>

  <script>
    const { createApp, ref, nextTick, onMounted, watch } = Vue;
    
    // 生成UUID函数
    function generateUUID() {
      return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        const r = Math.random() * 16 | 0;
        const v = c === 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
      });
    }
    
    const app = createApp({
      delimiters: ['[[', ']]'], // 使用自定义分隔符，避免与Jinja2冲突
      setup() {
        // 应用配置
        const APP_CONFIG = {
          API_ENDPOINT: "/api/v1/test/completions",
          STORAGE_KEY: 'chatSessions',
          MAX_TITLE_LENGTH: 30
        };
        
        // 从后端获取用户名和token
        const backendUsername = '{{ username }}';
        const backendToken = '{{ token }}';
        
        // 响应式状态
        const chatSessions = ref([]);
        const currentChatId = ref(null);
        const messageIdCounter = ref(0);
        const isLoading = ref(false);
        const userInput = ref('');
        const showSidebar = ref(window.innerWidth > 768);
        const chatMessages = ref(null);
        const isDarkMode = ref(localStorage.getItem('darkMode') === 'true');
        const lastAssistantMessage = ref(null);
        const username = ref(backendUsername || '访客用户');
        const token = backendToken;
        const selectedModel = ref('qwen32b');
        const abortController = ref(null);
        const isDeepThinking = ref(true);
        const isOnlineSearch = ref(false);
        
        // 监听模型选择变化
        // 监听模型选择变化
        watch(selectedModel, (newModel) => {
          localStorage.setItem('selectedModel', newModel);
        });
        
        // 模型选择变化处理
        function onModelChange() {
          console.log('模型已切换为:', selectedModel.value);
        }
        
        // 切换深度思考模式
        function toggleDeepThinking() {
          isDeepThinking.value = !isDeepThinking.value;
          console.log('深度思考模式:', isDeepThinking.value ? '开启' : '关闭');
        }
        
        // 切换联网搜索模式
        function toggleOnlineSearch() {
          isOnlineSearch.value = !isOnlineSearch.value;
          console.log('联网搜索模式:', isOnlineSearch.value ? '开启' : '关闭');
        }
        
        // 聊天会话类
        class ChatSession {
          constructor(id, title = '新对话', uuid = null) {
            this.id = id;
            this.uuid = uuid || generateUUID(); // 如果没有提供uuid，则生成新的
            this.title = title;
            this.messages = [];
            this.createdAt = new Date();
            this.updatedAt = new Date();
          }

          addMessage(role, content, reasoning_content = '') {
            const message = {
              id: ++messageIdCounter.value,
              role,
              content,
              reasoning_content,
              timestamp: new Date()
            };
            
            this.messages.push(message);
            this.updatedAt = new Date();
            
            // 如果是第一条用户消息，用它作为标题
            if (this.messages.length === 1 && role === 'user') {
              this.title = content.length > APP_CONFIG.MAX_TITLE_LENGTH 
                ? content.substring(0, APP_CONFIG.MAX_TITLE_LENGTH) + '...' 
                : content;
            }
            
            return message;
          }

          getLastUserMessage() {
            for (let i = this.messages.length - 1; i >= 0; i--) {
              if (this.messages[i].role === 'user') {
                return this.messages[i];
              }
            }
            return null;
          }
        }

        // 初始化markdown-it
        const md = window.markdownit({
          html: true,
          breaks: true,
          linkify: true,
          typographer: true,
          // 启用表格支持
          tables: true
        });
        
        // 完全重写表格处理函数，采用更直接的方法
        function fixMarkdownTable(text) {
          if (!text) return text;
          
          // 将文本按行分割
          const lines = text.split('\n');
          const result = [];
          
          // 表格状态跟踪
          let inTable = false;
          let tableStartIndex = -1;
          let headerRowIndex = -1;
          
          // 遍历每一行
          for (let i = 0; i < lines.length; i++) {
            const line = lines[i];
            const trimmedLine = line.trim();
            
            // 检测表格开始（以 | 开头和结尾的行）
            if (!inTable && trimmedLine.startsWith('|') && trimmedLine.endsWith('|')) {
              inTable = true;
              tableStartIndex = result.length;
              headerRowIndex = result.length;
              result.push(line); // 添加表头行
              continue;
            }
            
            // 如果在表格中，检查是否是分隔行或数据行
            if (inTable) {
              // 如果当前行不是表格格式，表格结束
              if (!trimmedLine.startsWith('|') || !trimmedLine.endsWith('|')) {
                inTable = false;
                result.push(line); // 添加非表格行
                continue;
              }
              
              // 如果是表格的第二行，检查是否需要插入或替换分隔行
              if (result.length === tableStartIndex + 1) {
                // 计算表头的列数
                const headerLine = result[headerRowIndex];
                const headerCells = headerLine.split('|').filter(cell => cell.trim() !== '');
                const columnCount = headerCells.length;
                
                // 创建标准分隔行
                const separatorCells = Array(columnCount).fill('---');
                const standardSeparator = '|' + separatorCells.join('|') + '|';
                
                // 检查当前行是否是分隔行（包含 - 字符）
                if (trimmedLine.includes('-')) {
                  // 替换为标准分隔行
                  result.push(standardSeparator);
                } else {
                  // 插入标准分隔行，然后添加当前行
                  result.push(standardSeparator);
                  result.push(line);
                }
              } else {
                // 添加表格的其他行
                result.push(line);
              }
            } else {
              // 不在表格中，直接添加行
              result.push(line);
            }
          }
          
          // 将结果重新组合为文本
          return result.join('\n');
        }

        // 格式化时间
        function formatTime(date) {
          if (!date) return '';
          const now = new Date();
          const messageDate = new Date(date);
          
          // 如果是今天的消息，只显示时间
          if (now.toDateString() === messageDate.toDateString()) {
            return messageDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
          }
          
          // 否则显示日期和时间
          return messageDate.toLocaleDateString() + ' ' + 
                 messageDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
        }

        // 渲染 Markdown
        function renderMarkdown(text) {
          if (!text) return '';
          
          try {
            // 预处理表格格式
            const fixedText = fixMarkdownTable(text);
            
            // 处理分隔线 --- 确保它们被正确渲染
            // 将独立的 --- 行转换为标准的 Markdown 分隔线格式
            const processedText = fixedText.replace(/^(\s*-{3,}\s*)$/gm, '\n---\n');
            
            // 使用markdown-it渲染Markdown
            let html = md.render(processedText);
            
            // 使用DOMPurify清理HTML，防止XSS攻击
            if (typeof DOMPurify !== 'undefined') {
              html = DOMPurify.sanitize(html);
            }
            
            return html;
          } catch (error) {
            console.error('Markdown渲染错误:', error);
            // 如果渲染失败，返回原始文本
            return text.replace(/\n/g, '<br>');
          }
        }

        // 获取当前聊天会话
        function getCurrentChat() {
          if (!currentChatId.value) return null;
          return chatSessions.value.find(chat => chat.id === currentChatId.value);
        }

        // 获取最后一条消息的ID
        function getLastMessageId() {
          const chat = getCurrentChat();
          if (!chat || chat.messages.length === 0) return null;
          return chat.messages[chat.messages.length - 1].id;
        }

        // 创建新的聊天会话
        function createNewChat() {
          // 检查是否已经存在一个空的"新对话"
          const existingEmptyChat = chatSessions.value.find(chat => 
            chat.title === '新对话' && chat.messages.length === 0
          );
          
          if (existingEmptyChat) {
            // 如果已经存在空的新对话，直接切换到它
            currentChatId.value = existingEmptyChat.id;
          } else {
            // 否则创建一个新的对话
            const newChatId = Date.now().toString();
            const newChat = new ChatSession(newChatId);
            chatSessions.value.unshift(newChat);
            currentChatId.value = newChatId;
          }
          
          saveChatSessions();
        }

        // 切换到指定聊天会话
        function switchToChat(chatId) {
          currentChatId.value = chatId;
          // 在移动设备上，切换聊天后隐藏侧边栏
          if (window.innerWidth <= 768) {
            showSidebar.value = false;
          }
        }

        // 清除当前聊天会话
        function clearCurrentChat() {
          const chat = getCurrentChat();
          if (chat) {
            // 从聊天会话列表中移除当前会话
            const index = chatSessions.value.findIndex(c => c.id === currentChatId.value);
            if (index !== -1) {
              chatSessions.value.splice(index, 1);
              
              // 如果还有其他会话，切换到第一个会话
              if (chatSessions.value.length > 0) {
                currentChatId.value = chatSessions.value[0].id;
              } else {
                // 如果没有其他会话，创建一个新的
                createNewChat();
              }
              
              saveChatSessions();
            }
          }
        }

        // 切换深色模式
        function toggleDarkMode() {
          isDarkMode.value = !isDarkMode.value;
          document.body.classList.toggle('dark-mode', isDarkMode.value);
          localStorage.setItem('darkMode', isDarkMode.value);
        }

        // 处理回车键
        function handleEnterKey(event) {
          // 如果按下Shift+Enter，则插入换行符
          if (event.shiftKey) {
            return;
          }
          
          // 否则发送消息
          sendMessage();
        }

        // 发送消息或停止流式输出
        async function sendMessage() {
          // 如果正在加载中，则停止流式输出
          if (isLoading.value) {
            stopStreaming();
            return;
          }
          
          if (!userInput.value.trim()) return;
          
          // 如果没有当前会话，创建一个新的
          if (!currentChatId.value) {
            createNewChat();
          }
          
          const chat = getCurrentChat();
          const userMessage = userInput.value.trim();
          
          // 添加用户消息
          chat.addMessage('user', userMessage);
          
          // 清空输入框
          userInput.value = '';
          
          // 添加一个空的助手消息，用于流式显示
          const assistantMessage = chat.addMessage('assistant', '', '');
          
          // 保存会话
          saveChatSessions();
          
          // 滚动到底部
          await nextTick();
          scrollToBottom();
          
          // 设置加载状态
          isLoading.value = true;
          
          // 创建新的AbortController
          abortController.value = new AbortController();
          
          try {
            // 准备请求数据
            // 添加获取API消息的方法到ChatSession类
            ChatSession.prototype.getApiMessages = function() {
              return this.messages
                .filter(msg => msg.id !== assistantMessage.id) // 排除刚添加的空助手消息
                .map(msg => ({
                  role: msg.role,
                  content: msg.content
                }));
            };
            
            const currentChat = getCurrentChat();
            
            // 使用选择的模型
            // 使用选择的模型
            const requestData = {
              model: selectedModel.value,
              messages: currentChat.getApiMessages(),
              stream: true,
              is_thinking: isDeepThinking.value,
              max_tokens: 10240,
              presence_penalty: 0.1,
              online_search: isOnlineSearch.value,
              requestid: Date.now().toString(),
              sessionid: currentChat.uuid // 使用UUID格式的会话ID
            };
            
            // 发送请求，添加signal参数用于中断
            const response = await fetch(APP_CONFIG.API_ENDPOINT, {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json',
                'token': token
              },
              body: JSON.stringify(requestData),
              signal: abortController.value.signal
            });
            
            if (!response.ok) {
              throw new Error(`HTTP error! status: ${response.status}`);
            }
            
            // 处理流式响应 (SSE格式)
            const reader = response.body.getReader();
            const decoder = new TextDecoder();
            let responseText = '';
            let reasoningText = '';
            let buffer = '';
            
            while (true) {
              const { done, value } = await reader.read();
              
              if (done) {
                break;
              }
              
              // 解码当前块
              buffer += decoder.decode(value, { stream: true });
              
              // 处理SSE格式数据 (data: {...})
              const lines = buffer.split('\n');
              buffer = lines.pop() || ''; // 保留最后一个可能不完整的行
              
              for (const line of lines) {
                if (line.startsWith('data: ')) {
                  try {
                    // 提取JSON部分
                    const jsonStr = line.substring(6); // 去掉 "data: "
                    const data = JSON.parse(jsonStr);
                    
                    // 提取内容增量和推理内容
                    if (data.choices && data.choices[0] && data.choices[0].delta) {
                      const delta = data.choices[0].delta;
                      
                      // 处理推理内容 - 累加显示
                      if (delta.reasoning_content) {
                        reasoningText += delta.reasoning_content;
                        const assistantMsg = chat.messages.find(msg => msg.id === assistantMessage.id);
                        if (assistantMsg) {
                          assistantMsg.reasoning_content = reasoningText;
                        }
                        
                        // 推理内容更新后，滚动到底部
                        await nextTick();
                        scrollReasoningToBottom();
                      }
                      
                      // 处理正式回答内容
                      if (delta.content) {
                        const contentDelta = delta.content;
                        responseText += contentDelta;
                        
                        // 更新助手消息内容
                        const assistantMsg = chat.messages.find(msg => msg.id === assistantMessage.id);
                        if (assistantMsg) {
                          assistantMsg.content = responseText;
                        }
                        console.log('contentDelta:', contentDelta);
                      }
                      
                      // 每收到新内容就更新UI
                      await nextTick();
                      scrollToBottom();
                    }
                  } catch (e) {
                    console.error('解析SSE数据出错:', e, line);
                  }
                }
              }
            }
            
            // 保存最终结果
            saveChatSessions();
            
          } catch (error) {
            console.error('Error fetching response:', error);
            
            // 检查是否是用户主动取消
            if (error.name === 'AbortError') {
              console.log('用户停止了流式输出');
              // 更新助手消息，保留已接收的内容
              const assistantMsg = chat.messages.find(msg => msg.id === assistantMessage.id);
              if (assistantMsg && !assistantMsg.content.trim()) {
                assistantMsg.content = '输出已停止';
              }
            } else {
              // 其他错误
              const assistantMsg = chat.messages.find(msg => msg.id === assistantMessage.id);
              if (assistantMsg) {
                assistantMsg.content = '抱歉，发生了错误，请稍后再试。';
                assistantMsg.reasoning_content = '';
              }
            }
            saveChatSessions();
          } finally {
            isLoading.value = false;
            abortController.value = null;
            await nextTick();
            scrollToBottom();
          }
        }

        // 停止流式输出
        function stopStreaming() {
          if (abortController.value) {
            abortController.value.abort();
            console.log('已停止流式输出');
          }
        }

        // 保存聊天会话到本地存储
        function saveChatSessions() {
          const sessionsData = chatSessions.value.map(chat => ({
            id: chat.id,
            uuid: chat.uuid,
            title: chat.title,
            messages: chat.messages.map(msg => ({
              id: msg.id,
              role: msg.role,
              content: msg.content,
              reasoning_content: msg.reasoning_content || '',
              timestamp: msg.timestamp
            })),
            createdAt: chat.createdAt,
            updatedAt: chat.updatedAt
          }));
          
          localStorage.setItem(APP_CONFIG.STORAGE_KEY, JSON.stringify(sessionsData));
        }

        // 从本地存储加载聊天会话
        function loadChatSessions() {
          const sessionsData = localStorage.getItem(APP_CONFIG.STORAGE_KEY);
          if (!sessionsData) return;
          
          try {
            const parsedData = JSON.parse(sessionsData);
            
            chatSessions.value = parsedData.map(chatData => {
              const chat = new ChatSession(chatData.id, chatData.title, chatData.uuid);
              chat.createdAt = new Date(chatData.createdAt);
              chat.updatedAt = new Date(chatData.updatedAt);
              
              chat.messages = chatData.messages.map(msgData => ({
                id: msgData.id,
                role: msgData.role,
                content: msgData.content,
                reasoning_content: msgData.reasoning_content || '',
                timestamp: new Date(msgData.timestamp)
              }));
              
              // 更新消息ID计数器
              const maxId = Math.max(...chat.messages.map(msg => msg.id), 0);
              if (maxId > messageIdCounter.value) {
                messageIdCounter.value = maxId;
              }
              
              return chat;
            });
            
            // 如果有聊天会话，默认选择第一个
            if (chatSessions.value.length > 0) {
              currentChatId.value = chatSessions.value[0].id;
            }
          } catch (error) {
            console.error('Error loading chat sessions:', error);
          }
        }

        // 滚动到底部
        function scrollToBottom() {
          if (chatMessages.value) {
            chatMessages.value.scrollTop = chatMessages.value.scrollHeight;
          }
        }

        // 滚动推理内容到底部
        function scrollReasoningToBottom() {
          nextTick(() => {
            // 滚动主聊天区域到底部
            scrollToBottom();
            
            // 滚动推理内容区域到底部
            const reasoningElements = document.querySelectorAll('.reasoning-text');
            if (reasoningElements.length > 0) {
              const lastReasoningElement = reasoningElements[reasoningElements.length - 1];
              lastReasoningElement.scrollTop = lastReasoningElement.scrollHeight;
            }
          });
        }

        // 监听窗口大小变化
        window.addEventListener('resize', () => {
          showSidebar.value = window.innerWidth > 768;
        });

        // 组件挂载时
        onMounted(() => {
          // 加载聊天会话
          loadChatSessions();
          
          // 应用深色模式
          if (isDarkMode.value) {
            document.body.classList.add('dark-mode');
          }
          
          // 如果没有聊天会话，创建一个新的
          if (chatSessions.value.length === 0) {
            createNewChat();
          }
          
          // 滚动到底部
          nextTick(() => {
            scrollToBottom();
          });
        });

        return {
          chatSessions,
          currentChatId,
          isLoading,
          userInput,
          showSidebar,
          chatMessages,
          isDarkMode,
          lastAssistantMessage,
          username,
          token,
          selectedModel,
          abortController,
          isDeepThinking,
          isOnlineSearch,
          getCurrentChat,
          getLastMessageId,
          createNewChat,
          switchToChat,
          clearCurrentChat,
          toggleDarkMode,
          handleEnterKey,
          sendMessage,
          stopStreaming,
          formatTime,
          renderMarkdown,
          onModelChange,
          toggleDeepThinking,
          toggleOnlineSearch
        };
      }
    });
    
    app.mount('#app');
  </script>
</body>
</html>